📖

请从左侧选择一篇笔记开始阅读

\n\n\n\n```\n\n---\n\n## \u4e94\u3001requestIdleCallback vs requestAnimationFrame\n\n| API | \u65f6\u673a | \u7528\u9014 |\n|-----|------|------|\n| `requestAnimationFrame` | \u6bcf\u5e27\u6e32\u67d3**\u4e4b\u524d** | \u52a8\u753b\u3001DOM \u66f4\u65b0 |\n| `requestIdleCallback` | \u6d4f\u89c8\u5668**\u7a7a\u95f2**\u65f6 | \u4f4e\u4f18\u5148\u7ea7\u4efb\u52a1\uff08\u4e0a\u62a5\u3001\u9884\u52a0\u8f7d\uff09 |\n\n---\n\n## \u516d\u3001\u9ad8\u9891\u9762\u8bd5\u9898\n\n### Q1\uff1a\u4e3a\u4ec0\u4e48 CSS \u8981\u653e head\uff0cJS \u8981\u653e body \u5e95\u90e8\uff1f\n\n> CSS \u662f\u6e32\u67d3\u963b\u585e\u8d44\u6e90\uff0c\u653e head \u8ba9\u9875\u9762\u5c3d\u65e9\u5b8c\u6210 CSSOM \u6784\u5efa\u3002JS \u9ed8\u8ba4\u4f1a\u963b\u585e HTML \u89e3\u6790\uff08script \u6807\u7b7e\u4f1a\u6682\u505c DOM \u6784\u5efa\uff09\uff0c\u653e\u5e95\u90e8\u51cf\u5c11\u767d\u5c4f\u65f6\u95f4\u3002\n\n### Q2\uff1adisplay:none \u7684\u5143\u7d20\u5728\u6e32\u67d3\u6811\u4e2d\u5417\uff1f\n> \u4e0d\u5728\u3002`display:none` \u4e0d\u751f\u6210\u76d2\u5b50\uff0c\u4e0d\u5728\u6e32\u67d3\u6811\u4e2d\u3002\u4f46 `visibility:hidden` \u5728\u6e32\u67d3\u6811\u4e2d\u3002\n\n### Q3\uff1a\u54ea\u4e9b\u64cd\u4f5c\u4f1a\u89e6\u53d1\u56de\u6d41\uff1f\n> \u589e\u5220 DOM\u3001\u4fee\u6539\u5143\u7d20\u5c3a\u5bf8/\u4f4d\u7f6e\u3001`offsetWidth/offsetHeight` \u7b49\u8bfb\u53d6\u4e5f\u4f1a**\u5f3a\u5236\u540c\u6b65\u5e03\u5c40**\u3002\n\n### Q4\uff1a\u4e3a\u4ec0\u4e48\u8bfb offsetWidth \u4f1a\u89e6\u53d1\u56de\u6d41\uff1f\n> \u8bfb\u53d6\u5e03\u5c40\u5c5e\u6027\u65f6\uff0c\u6d4f\u89c8\u5668\u5fc5\u987b\u4fdd\u969c\u503c\u662f\u6700\u65b0\u7684\uff0c\u82e5\u6709\u672a\u5e94\u7528\u7684\u6837\u5f0f\u53d8\u66f4\uff0c\u4f1a\u5148\u5f3a\u5236\u56de\u6d41\u518d\u8ba1\u7b97\uff0c\u8fd9\u662f\"\u5f3a\u5236\u540c\u6b65\u5e03\u5c40\"\uff08FSL\uff09\u3002\n\n---\n\n*\u6574\u7406\u65f6\u95f4\uff1a2026-06-03*\n", "08-HTTP": "# HTTP \u9762\u8bd5\u7b14\u8bb0\n\n---\n\n## \u4e00\u3001HTTP \u662f\u4ec0\u4e48\uff1f\n\n**\u8d85\u6587\u672c\u4f20\u8f93\u534f\u8bae**\uff08HyperText Transfer Protocol\uff09\uff0c\u7528\u4e8e\u5ba2\u6237\u7aef\u548c\u670d\u52a1\u5668\u4e4b\u95f4\u7684\u901a\u4fe1\u3002\n\n---\n\n## \u4e8c\u3001HTTP \u7248\u672c\u5bf9\u6bd4\n\n| \u7279\u6027 | HTTP/1.0 | HTTP/1.1 | HTTP/2 | HTTP/3 |\n|------|----------|----------|--------|--------|\n| \u8fde\u63a5\u65b9\u5f0f | \u77ed\u8fde\u63a5 | \u6301\u4e45\u8fde\u63a5 | \u591a\u8def\u590d\u7528 | \u591a\u8def\u590d\u7528\uff08\u57fa\u4e8e QUIC\uff09 |\n| \u961f\u5934\u963b\u585e | \u6709 | \u6709 | \u89e3\u51b3 | \u89e3\u51b3 |\n| \u5934\u90e8\u538b\u7f29 | \u274c | \u274c | HPACK | QPACK |\n| \u670d\u52a1\u5668\u63a8\u9001 | \u274c | \u274c | \u2705 | \u2705 |\n| \u4f20\u8f93\u534f\u8bae | TCP | TCP | TCP | UDP(QUIC) |\n| \u63a8\u51fa\u5e74\u4efd | 1996 | 1999 | 2015 | 2022 |\n\n---\n\n## \u4e09\u3001HTTP 1.1 vs HTTP 2.0\n\n### HTTP/1.1 \u7684\u95ee\u9898\n- **\u961f\u5934\u963b\u585e**\uff1a\u5355\u4e2a TCP \u8fde\u63a5\u4e0a\u8bf7\u6c42\u5fc5\u987b\u6392\u961f\n- **\u5934\u90e8\u5197\u4f59**\uff1a\u6bcf\u6b21\u8bf7\u6c42\u90fd\u5e26\u5b8c\u6574\u5934\u90e8\n\n### HTTP/2 \u7684\u6539\u8fdb\n\n```yaml\n\u591a\u8def\u590d\u7528: \u4e00\u4e2a\u8fde\u63a5\u5e76\u53d1\u591a\u4e2a\u8bf7\u6c42/\u54cd\u5e94\n\u4e8c\u8fdb\u5236\u5206\u5e27: \u62a5\u6587\u5207\u5272\u4e3a\u4e8c\u8fdb\u5236\u5e27\uff0c\u4ea4\u9519\u4f20\u8f93\n\u5934\u90e8\u538b\u7f29 (HPACK): \u7d22\u5f15\u8868\u538b\u7f29\u91cd\u590d\u5934\u90e8\n\u670d\u52a1\u5668\u63a8\u9001: \u670d\u52a1\u7aef\u4e3b\u52a8\u63a8\u9001\u8d44\u6e90\n```\n\n---\n\n## \u56db\u3001HTTPS = HTTP + TLS/SSL\n\n### \u52a0\u5bc6\u8fc7\u7a0b\n\n```\n\u5ba2\u6237\u7aef \u2192 Server Hello \u2192 \u670d\u52a1\u7aef\n \u2193\n \u8bc1\u4e66\u9a8c\u8bc1 + \u9884\u4e3b\u5bc6\u94a5\u52a0\u5bc6\n \u2193\n \u53cc\u65b9\u751f\u6210\u4f1a\u8bdd\u5bc6\u94a5\uff08\u5bf9\u79f0\u52a0\u5bc6\uff09\n \u2193\n \u5f00\u59cb\u52a0\u5bc6\u901a\u4fe1\n```\n\n### \u4e09\u79cd HTTP\n\n| \u65b9\u5f0f | \u539f\u7406 | \u7279\u70b9 |\n|------|------|------|\n| HTTP | \u660e\u6587\u4f20\u8f93 | \u4e0d\u5b89\u5168 |\n| HTTPS | HTTP + TLS \u52a0\u5bc6 | \u5b89\u5168\uff0c\u6709\u8bc1\u4e66 |\n| HTTP/3 | QUIC\uff08UDP\uff09+ TLS 1.3 | 0-RTT \u5efa\u8fde\uff0c\u4f4e\u5ef6\u8fdf |\n\n---\n\n## \u4e94\u3001HTTP \u72b6\u6001\u7801\n\n| \u8303\u56f4 | \u542b\u4e49 | \u5e38\u7528 |\n|------|------|------|\n| 1xx | \u4fe1\u606f | 101 \u534f\u8bae\u5207\u6362\uff08WebSocket\uff09 |\n| 2xx | \u6210\u529f | 200 OK\uff0c201 Created\uff0c204 No Content |\n| 3xx | \u91cd\u5b9a\u5411 | 301 \u6c38\u4e45\uff0c302 \u4e34\u65f6\uff0c304 \u672a\u4fee\u6539 |\n| 4xx | \u5ba2\u6237\u7aef\u9519\u8bef | 400 Bad Request\uff0c401 \u672a\u6388\u6743\uff0c403 \u7981\u6b62\uff0c404 \u4e0d\u5b58\u5728 |\n| 5xx | \u670d\u52a1\u7aef\u9519\u8bef | 500 \u670d\u52a1\u5668\u9519\u8bef\uff0c502 \u7f51\u5173\u9519\uff0c503 \u670d\u52a1\u4e0d\u53ef\u7528 |\n\n---\n\n## \u516d\u3001\u8bf7\u6c42\u65b9\u6cd5\n\n| \u65b9\u6cd5 | \u7528\u9014 | \u5e42\u7b49 | \u5b89\u5168 |\n|------|------|------|------|\n| GET | \u83b7\u53d6\u8d44\u6e90 | \u2705 | \u2705 |\n| POST | \u521b\u5efa\u8d44\u6e90 | \u274c | \u274c |\n| PUT | \u5b8c\u6574\u66f4\u65b0 | \u2705 | \u274c |\n| PATCH | \u90e8\u5206\u66f4\u65b0 | \u274c | \u274c |\n| DELETE | \u5220\u9664\u8d44\u6e90 | \u2705 | \u274c |\n| HEAD | \u83b7\u53d6\u5934\u90e8 | \u2705 | \u2705 |\n| OPTIONS | \u9884\u68c0\u8bf7\u6c42 | \u2705 | \u2705 |\n\n---\n\n## \u4e03\u3001GET vs POST\n\n| \u5bf9\u6bd4 | GET | POST |\n|------|-----|------|\n| \u53c2\u6570\u4f4d\u7f6e | URL \u67e5\u8be2\u4e32 | \u8bf7\u6c42\u4f53 |\n| \u957f\u5ea6\u9650\u5236 | \u7ea6 2KB\uff08\u6d4f\u89c8\u5668\u9650\u5236\uff09 | \u65e0 |\n| \u7f13\u5b58 | \u53ef\u7f13\u5b58 | \u4e0d\u7f13\u5b58 |\n| \u4e66\u7b7e | \u53ef\u6536\u85cf | \u4e0d\u53ef\u6536\u85cf |\n| \u5b89\u5168\u6027 | URL \u660e\u6587 | \u76f8\u5bf9\u5b89\u5168\uff08HTTPS \u90fd\u52a0\u5bc6\uff09 |\n\n---\n\n## \u516b\u3001Cookie / Session / Token / JWT\n\n| \u6280\u672f | \u5b58\u54ea | \u7279\u70b9 |\n|------|------|------|\n| Cookie | \u6d4f\u89c8\u5668 | \u81ea\u52a8\u5e26\u5728\u8bf7\u6c42\u5934\uff0c\u6709\u5927\u5c0f\u9650\u5236\uff084KB\uff09 |\n| Session | \u670d\u52a1\u7aef | \u57fa\u4e8e Cookie \u7684 SessionID\uff0c\u670d\u52a1\u7aef\u5b58\u50a8 |\n| Token | \u5ba2\u6237\u7aef | \u65e0\u72b6\u6001\uff0c\u670d\u52a1\u7aef\u53ea\u9700\u9a8c\u8bc1\u7b7e\u540d |\n| JWT | \u5ba2\u6237\u7aef | Header.Payload.Signature \u4e09\u6bb5\u5f0f\uff0c\u81ea\u5305\u542b |\n\n---\n\n## \u4e5d\u3001\u5f3a\u7f13\u5b58 & \u534f\u5546\u7f13\u5b58\n\n```yaml\n\u5f3a\u7f13\u5b58\uff08\u4e0d\u8bf7\u6c42\u670d\u52a1\u5668\uff09:\n - Expires\uff08\u7edd\u5bf9\u65f6\u95f4\uff0cHTTP/1.0\uff09\n - Cache-Control: max-age=3600\uff08\u76f8\u5bf9\u65f6\u95f4\uff0c\u4f18\u5148\u7ea7\u9ad8\uff09\n\n\u534f\u5546\u7f13\u5b58\uff08\u95ee\u670d\u52a1\u5668\uff09:\n - Last-Modified / If-Modified-Since\uff08\u79d2\u7ea7\u7cbe\u5ea6\uff09\n - ETag / If-None-Match\uff08\u7cbe\u786e\uff0c\u4f18\u5148\u7ea7\u9ad8\uff09\n```\n\n---\n\n## \u5341\u3001\u9ad8\u9891\u9762\u8bd5\u9898\n\n### Q1\uff1aHTTP/2 \u5982\u4f55\u89e3\u51b3\u961f\u5934\u963b\u585e\uff1f\n> \u901a\u8fc7**\u591a\u8def\u590d\u7528**\uff0c\u5728\u4e00\u4e2a TCP \u8fde\u63a5\u4e0a\u5e76\u53d1\u591a\u4e2a stream\uff0c\u6bcf\u4e2a stream \u72ec\u7acb\u4f20\u8f93\uff0c\u4e0d\u4e92\u76f8\u963b\u585e\u3002\u4f46 TCP \u5c42\u9762\u7684\u961f\u5934\u963b\u585e\u4ecd\u5b58\u5728\uff08HTTP/3 \u7528 QUIC \u89e3\u51b3\uff09\u3002\n\n### Q2\uff1a\u4ece\u8f93\u5165 URL \u5230\u9875\u9762\u5c55\u793a\u53d1\u751f\u4e86\u4ec0\u4e48\uff1f\n> DNS \u89e3\u6790 \u2192 TCP \u4e09\u6b21\u63e1\u624b \u2192 TLS \u63e1\u624b(HTTPS) \u2192 \u53d1\u9001 HTTP \u8bf7\u6c42 \u2192 \u670d\u52a1\u5668\u5904\u7406\u8fd4\u56de \u2192 \u6d4f\u89c8\u5668\u89e3\u6790 HTML \u2192 \u6784\u5efa DOM/CSSOM \u2192 \u6e32\u67d3 \u2192 \u663e\u793a\n\n### Q3\uff1a304 \u548c 200 \u7f13\u5b58\u6709\u4ec0\u4e48\u533a\u522b\uff1f\n> 304 \u8868\u793a\u8d44\u6e90\u672a\u4fee\u6539\uff0c\u6d4f\u89c8\u5668\u7528\u672c\u5730\u7f13\u5b58\uff0c\u4e0d\u4f20\u54cd\u5e94\u4f53\uff1b200 \u8fd4\u56de\u5b8c\u6574\u5185\u5bb9\u3002304 \u72b6\u6001\u5fc5\u987b\u7ed3\u5408\u534f\u5546\u7f13\u5b58\uff08ETag/Last-Modified\uff09\u4f7f\u7528\u3002\n\n### Q4\uff1aTCP \u4e09\u6b21\u63e1\u624b\u8fc7\u7a0b\uff1f\n> SYN \u2192 SYN+ACK \u2192 ACK\u3002\u76ee\u7684\uff1a\u786e\u8ba4\u53cc\u65b9\u6536\u53d1\u80fd\u529b\u6b63\u5e38\uff0c\u540c\u6b65\u521d\u59cb\u5e8f\u5217\u53f7\u3002\n\n### Q5\uff1aJWT \u7684\u7ed3\u6784\u662f\u4ec0\u4e48\uff1f\n> `Header.Payload.Signature`\u3002\u524d\u4e24\u90e8\u5206 Base64 \u7f16\u7801\uff0c\u7b2c\u4e09\u90e8\u5206\u7528 secret \u7b7e\u540d\u3002\u53ef\u81ea\u5305\u542b\u7528\u6237\u4fe1\u606f\uff0c\u4f46\u4e0d\u53ef\u5b58\u653e\u654f\u611f\u6570\u636e\uff08\u524d\u4e24\u90e8\u5206\u53ef\u89e3\u7801\uff09\u3002\n\n---\n\n*\u6574\u7406\u65f6\u95f4\uff1a2026-06-03*\n", "09-\u7b97\u6cd5": "# \u524d\u7aef\u9ad8\u9891\u7b97\u6cd5\u9762\u8bd5\u7b14\u8bb0\n\n---\n\n## \u4e00\u3001\u6392\u5e8f\u7b97\u6cd5\n\n### 1. \u5feb\u6392\uff08Quick Sort\uff09\n\n```js\nfunction quickSort(arr) {\n if (arr.length <= 1) return arr\n const pivot = arr[0]\n const left = arr.slice(1).filter(x => x < pivot)\n const right = arr.slice(1).filter(x => x >= pivot)\n return [...quickSort(left), pivot, ...quickSort(right)]\n}\n// \u65f6\u95f4 O(nlogn)\uff0c\u7a7a\u95f4 O(nlogn)\uff0c\u4e0d\u7a33\u5b9a\n```\n\n### 2. \u5f52\u5e76\u6392\u5e8f\uff08Merge Sort\uff09\n\n```js\nfunction mergeSort(arr) {\n if (arr.length <= 1) return arr\n const mid = Math.floor(arr.length / 2)\n const left = mergeSort(arr.slice(0, mid))\n const right = mergeSort(arr.slice(mid))\n return merge(left, right)\n}\nfunction merge(a, b) {\n const res = []\n while (a.length && b.length) res.push(a[0] < b[0] ? a.shift() : b.shift())\n return [...res, ...a, ...b]\n}\n// \u65f6\u95f4 O(nlogn)\uff0c\u7a7a\u95f4 O(n)\uff0c\u7a33\u5b9a\n```\n\n---\n\n## \u4e8c\u3001\u6570\u7ec4\u53bb\u91cd\n\n```js\n// \u65b9\u5f0f1\uff1aSet\uff08\u63a8\u8350\uff09\n[...new Set(arr)]\n\n// \u65b9\u5f0f2\uff1afilter + indexOf\narr.filter((v, i) => arr.indexOf(v) === i)\n\n// \u65b9\u5f0f3\uff1a\u5bf9\u8c61\u6570\u7ec4\u6309\u5b57\u6bb5\u53bb\u91cd\nconst dedup = (arr, key) => {\n const seen = new Map()\n return arr.filter(v => !seen.has(v[key]) && seen.set(v[key], 1))\n}\n```\n\n---\n\n## \u4e09\u3001\u6241\u5e73\u5316\u6570\u7ec4\n\n```js\n// \u65b9\u5f0f1\uff1aflat\uff08ES2019\uff09\nconst flat = arr.flat(Infinity)\n\n// \u65b9\u5f0f2\uff1a\u9012\u5f52\nfunction flatten(arr) {\n return arr.reduce((acc, val) =>\n acc.concat(Array.isArray(val) ? flatten(val) : val), []\n )\n}\n\n// \u65b9\u5f0f3\uff1a\u6808\u5faa\u73af\uff08\u975e\u9012\u5f52\uff09\nfunction flatten(arr) {\n const stack = [...arr], res = []\n while (stack.length) {\n const cur = stack.pop()\n Array.isArray(cur) ? stack.push(...cur) : res.unshift(cur)\n }\n return res\n}\n```\n\n---\n\n## \u56db\u3001\u9632\u6296 & \u8282\u6d41\n\n### \u9632\u6296\uff08Debounce\uff09\n\n```js\nfunction debounce(fn, delay) {\n let timer = null\n return function(...args) {\n clearTimeout(timer)\n timer = setTimeout(() => fn.apply(this, args), delay)\n }\n}\n// \u573a\u666f\uff1a\u641c\u7d22\u8f93\u5165\u3001\u7a97\u53e3resize\n```\n\n### \u8282\u6d41\uff08Throttle\uff09\n\n```js\nfunction throttle(fn, delay) {\n let last = 0\n return function(...args) {\n const now = Date.now()\n if (now - last >= delay) {\n last = now\n fn.apply(this, args)\n }\n }\n}\n// \u573a\u666f\uff1a\u6eda\u52a8\u3001\u9f20\u6807\u79fb\u52a8\n```\n\n---\n\n## \u4e94\u3001\u6df1\u6d45\u62f7\u8d1d\n\n```js\n// \u6d45\u62f7\u8d1d\nconst copy = { ...obj }\nconst copy = Object.assign({}, obj)\n\n// \u6df1\u62f7\u8d1d\uff08\u57fa\u7840\u7248\uff09\nfunction deepClone(obj, map = new WeakMap()) {\n if (typeof obj !== 'object' || obj === null) return obj\n if (map.has(obj)) return map.get(obj) // \u5faa\u73af\u5f15\u7528\n\n const result = Array.isArray(obj) ? [] : {}\n map.set(obj, result)\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n result[key] = deepClone(obj[key], map)\n }\n }\n return result\n}\n\n// \u4e00\u884c\u6df1\u62f7\u8d1d\uff08\u65e0\u51fd\u6570/\u5faa\u73af\u5f15\u7528\u573a\u666f\uff09\nconst copy = JSON.parse(JSON.stringify(obj))\n// \u7f3a\u9677\uff1a\u4e22\u5931 undefined\u3001\u51fd\u6570\u3001Symbol\u3001\u5faa\u73af\u5f15\u7528\n```\n\n---\n\n## \u516d\u3001\u6570\u7ec4\u76f8\u5173\n\n### \u4e24\u6570\u4e4b\u548c\n\n```js\nfunction twoSum(nums, target) {\n const map = new Map()\n for (let i = 0; i < nums.length; i++) {\n const diff = target - nums[i]\n if (map.has(diff)) return [map.get(diff), i]\n map.set(nums[i], i)\n }\n}\n```\n\n### \u65e0\u91cd\u590d\u5b57\u7b26\u7684\u6700\u957f\u5b50\u4e32\n\n```js\nfunction lengthOfLongestSubstring(s) {\n let l = 0, max = 0\n const set = new Set()\n for (let r = 0; r < s.length; r++) {\n while (set.has(s[r])) set.delete(s[l++])\n set.add(s[r])\n max = Math.max(max, r - l + 1)\n }\n return max\n}\n```\n\n---\n\n## \u4e03\u3001\u590d\u6742\u5ea6\u901f\u67e5\u8868\n\n| \u7b97\u6cd5 | \u65f6\u95f4\uff08\u5e73\u5747\uff09 | \u7a7a\u95f4 | \u7a33\u5b9a |\n|------|-------------|------|------|\n| \u5feb\u6392 | O(nlogn) | O(logn) | \u274c |\n| \u5f52\u5e76 | O(nlogn) | O(n) | \u2705 |\n| \u5192\u6ce1 | O(n\u00b2) | O(1) | \u2705 |\n| \u63d2\u5165 | O(n\u00b2) | O(1) | \u2705 |\n| \u4e8c\u5206\u67e5\u627e | O(logn) | O(1) | - |\n| \u54c8\u5e0c\u67e5\u627e | O(1) | O(n) | - |\n\n---\n\n## \u516b\u3001\u9ad8\u9891\u9762\u8bd5\u9898\n\n### Q1\uff1a\u9632\u6296\u548c\u8282\u6d41\u7684\u533a\u522b\uff1f\n> \u9632\u6296\uff1a\u8fde\u7eed\u89e6\u53d1\u53ea\u6267\u884c**\u6700\u540e\u4e00\u6b21**\uff08\u7b49\u505c\u6b62\u89e6\u53d1\u540e\u5ef6\u8fdf\u6267\u884c\uff09\u3002\u8282\u6d41\uff1a\u56fa\u5b9a\u65f6\u95f4\u95f4\u9694**\u53ea\u6267\u884c\u4e00\u6b21**\uff08\u7a00\u91ca\u6267\u884c\u9891\u7387\uff09\u3002\n\n### Q2\uff1a\u4ec0\u4e48\u662f\u7a33\u5b9a\u6392\u5e8f\uff1f\n> \u76f8\u7b49\u5143\u7d20\u7684\u76f8\u5bf9\u987a\u5e8f\u5728\u6392\u5e8f\u540e\u4fdd\u6301\u4e0d\u53d8\u3002\n\n### Q3\uff1a\u6df1\u62f7\u8d1d\u5982\u4f55\u5904\u7406\u5faa\u73af\u5f15\u7528\uff1f\n> \u4f7f\u7528 `WeakMap` \u7f13\u5b58\u5df2\u62f7\u8d1d\u5bf9\u8c61\uff0c\u9047\u5230\u5faa\u73af\u5f15\u7528\u76f4\u63a5\u8fd4\u56de\u7f13\u5b58\u7684\u5f15\u7528\u3002\n\n### Q4\uff1a\u4e3a\u4ec0\u4e48\u7528 WeakMap \u800c\u4e0d\u662f Map \u505a\u7f13\u5b58\uff1f\n> WeakMap \u7684\u952e\u662f\u5f31\u5f15\u7528\uff0c\u4e0d\u5f71\u54cd GC\u3002\u5982\u679c\u7528 Map\uff0c\u88ab\u62f7\u8d1d\u5bf9\u8c61\u5373\u4f7f\u5916\u90e8\u5df2\u65e0\u5f15\u7528\u4e5f\u65e0\u6cd5\u88ab\u56de\u6536\u3002\n\n---\n\n*\u6574\u7406\u65f6\u95f4\uff1a2026-06-03*\n", "10-CSS\u5e03\u5c40": "# CSS \u5e03\u5c40\u9762\u8bd5\u7b14\u8bb0\n\n---\n\n## \u4e00\u3001\u76d2\u6a21\u578b\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 margin \u2502\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502 \u2502 border \u2502 \u2502\n\u2502 \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502\n\u2502 \u2502 \u2502 padding \u2502 \u2502 \u2502\n\u2502 \u2502 \u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502 \u2502\n\u2502 \u2502 \u2502 \u2502 content \u2502 \u2502 \u2502 \u2502\n\u2502 \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2502 \u2502\n\u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2502\n\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n### content-box vs border-box\n\n```css\n/* \u6807\u51c6\u76d2\u6a21\u578b\uff1awidth = content */\nbox-sizing: content-box; /* \u9ed8\u8ba4 */\n\n/* IE \u76d2\u6a21\u578b\uff1awidth = content + padding + border */\nbox-sizing: border-box; /* \u63a8\u8350 */\n```\n\n---\n\n## \u4e8c\u3001BFC\uff08\u5757\u7ea7\u683c\u5f0f\u5316\u4e0a\u4e0b\u6587\uff09\n\n### \u662f\u4ec0\u4e48\uff1f\n\nBFC \u662f\u4e00\u4e2a**\u72ec\u7acb\u6e32\u67d3\u533a\u57df**\uff0c\u5185\u90e8\u5143\u7d20\u7684\u5e03\u5c40\u4e0d\u4f1a\u5f71\u54cd\u5916\u90e8\u3002\n\n### \u89e6\u53d1\u6761\u4ef6\n\n```css\noverflow: hidden / auto / scroll\ndisplay: flex / inline-flex / grid\nposition: absolute / fixed\nfloat: left / right\n```\n\n### \u89e3\u51b3\u7684\u95ee\u9898\n\n| \u95ee\u9898 | BFC \u65b9\u6848 |\n|------|----------|\n| \u5916\u8fb9\u8ddd\u6298\u53e0 | \u7236\u5143\u7d20\u89e6\u53d1 BFC |\n| \u6e05\u9664\u6d6e\u52a8 | \u7236\u5143\u7d20\u89e6\u53d1 BFC\uff08overflow: hidden\uff09 |\n| \u4e24\u680f\u5e03\u5c40 | \u6d6e\u52a8\u5143\u7d20 + BFC \u5144\u5f1f\u5143\u7d20 |\n\n```css\n/* \u89e3\u51b3\u5916\u8fb9\u8ddd\u6298\u53e0 */\n.parent { overflow: hidden }\n\n/* \u6e05\u9664\u6d6e\u52a8 */\n.clearfix::after {\n content: '';\n display: block;\n clear: both;\n}\n```\n\n---\n\n## \u4e09\u3001\u5c45\u4e2d\u65b9\u6848\n\n### \u6c34\u5e73\u5c45\u4e2d\n\n```css\n/* \u5757\u7ea7 */\n.element { margin: 0 auto }\n\n/* \u884c\u5185/\u6587\u672c */\n.parent { text-align: center }\n\n/* flex */\n.parent { display: flex; justify-content: center }\n```\n\n### \u5782\u76f4\u5c45\u4e2d\n\n```css\n/* \u5355\u884c\u6587\u672c */\n.element { line-height: \u7b49\u4e8eheight }\n\n/* flex */\n.parent { display: flex; align-items: center }\n\n/* grid */\n.parent { display: grid; place-items: center }\n```\n\n### \u6c34\u5e73\u5782\u76f4\u5c45\u4e2d\n\n```css\n/* flex\uff08\u63a8\u8350\uff09 */\n.parent {\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n/* \u7edd\u5bf9\u5b9a\u4f4d + transform\uff08\u672a\u77e5\u5bbd\u9ad8\uff09 */\n.child {\n position: absolute; top: 50%; left: 50%;\n transform: translate(-50%, -50%);\n}\n\n/* grid\uff08\u6700\u7b80\u6d01\uff09 */\n.parent { display: grid; place-items: center }\n```\n\n---\n\n## \u56db\u3001Flex \u5e03\u5c40\n\n```css\n.container {\n display: flex;\n /* \u4e3b\u8f74\u65b9\u5411 */\n flex-direction: row | column;\n /* \u4e3b\u8f74\u5bf9\u9f50 */\n justify-content: flex-start | center | space-between | space-around;\n /* \u4ea4\u53c9\u8f74\u5bf9\u9f50 */\n align-items: stretch | center | flex-start | flex-end;\n /* \u6362\u884c */\n flex-wrap: nowrap | wrap;\n}\n\n.item {\n flex: 1; /* flex-grow flex-shrink flex-basis \u7f29\u5199 */\n flex-grow: 1; /* \u653e\u5927\u6bd4\u4f8b */\n flex-shrink: 0; /* \u7f29\u5c0f\u6bd4\u4f8b */\n flex-basis: 200px; /* \u57fa\u51c6\u5927\u5c0f */\n}\n```\n\n### \u7ecf\u5178\uff1a\u4e24\u680f/\u4e09\u680f\u5e03\u5c40\n\n```css\n/* \u4e24\u680f\uff1a\u5de6\u4fa7\u56fa\u5b9a\uff0c\u53f3\u4fa7\u81ea\u9002\u5e94 */\n.container { display: flex }\n.left { width: 200px }\n.right { flex: 1 }\n\n/* \u4e09\u680f\uff1a\u5723\u676f/\u53cc\u98de\u7ffc = flex \u5934\u5c3e\u56fa\u5b9a + \u4e2d\u95f4\u81ea\u9002\u5e94 */\n.container { display: flex }\n.left, .right { width: 200px }\n.center { flex: 1 }\n```\n\n---\n\n## \u4e94\u3001Grid \u5e03\u5c40\n\n```css\n.container {\n display: grid;\n grid-template-columns: 200px 1fr 1fr; /* \u5217\u5b9a\u4e49 */\n grid-template-rows: auto 1fr;\n gap: 16px; /* \u95f4\u8ddd */\n grid-template-areas:\n \"header header header\"\n \"sidebar main main\";\n}\n```\n\n---\n\n## \u516d\u3001CSS \u9009\u62e9\u5668\u4f18\u5148\u7ea7\n\n```\n!important > \u5185\u8054 > #id > .class > \u6807\u7b7e > \u901a\u914d\u7b26\n\n\u5177\u4f53\u8ba1\u7b97 (a, b, c):\n a = \u5185\u8054\u6837\u5f0f\n b = ID \u9009\u62e9\u5668\u6570\u91cf\n c = class/\u5c5e\u6027/\u4f2a\u7c7b \u6570\u91cf\n d = \u6807\u7b7e/\u4f2a\u5143\u7d20 \u6570\u91cf\n\n/* (0,1,2,1) */\n#app .list li.active { ... }\n```\n\n---\n\n## \u4e03\u3001\u9ad8\u9891\u9762\u8bd5\u9898\n\n### Q1\uff1a\u4e3a\u4ec0\u4e48\u63a8\u8350 border-box\uff1f\n> \u8bbe\u7f6e\u5bbd\u9ad8\u65f6 padding/border \u5305\u542b\u5728\u5185\uff0c\u5e03\u5c40\u8ba1\u7b97\u66f4\u76f4\u89c2\u3002\u6bd4\u5982 `width: 200px` \u59cb\u7ec8\u662f 200px\uff0c\u4e0d\u7528\u62c5\u5fc3 padding \u6491\u5f00\u5bb9\u5668\u3002\n\n### Q2\uff1a\u9690\u85cf\u5143\u7d20\u6709\u54ea\u4e9b\u65b9\u5f0f\uff1f\n\n| \u65b9\u5f0f | \u5360\u7a7a\u95f4 | \u4e8b\u4ef6\u662f\u5426\u54cd\u5e94 |\n|------|--------|-------------|\n| `display: none` | \u274c | \u274c |\n| `visibility: hidden` | \u2705 | \u274c |\n| `opacity: 0` | \u2705 | \u2705 |\n| `position: absolute; left: -9999px` | \u274c | \u274c |\n\n### Q3\uff1aem / rem / vw / vh \u7684\u533a\u522b\uff1f\n\n| \u5355\u4f4d | \u76f8\u5bf9\u8c01 |\n|------|--------|\n| em | \u7236\u5143\u7d20\u5b57\u4f53\u5927\u5c0f |\n| rem | \u6839\u5143\u7d20(html)\u5b57\u4f53\u5927\u5c0f |\n| vw / vh | \u89c6\u53e3\u5bbd\u5ea6/\u9ad8\u5ea6\u7684 1% |\n\n---\n\n*\u6574\u7406\u65f6\u95f4\uff1a2026-06-03*\n", "11-\u6027\u80fd\u4f18\u5316": "# \u6027\u80fd\u4f18\u5316\u9762\u8bd5\u7b14\u8bb0\n\n---\n\n## \u4e00\u3001\u6027\u80fd\u6307\u6807\uff08Core Web Vitals\uff09\n\n| \u6307\u6807 | \u8861\u91cf | \u76ee\u6807 |\n|------|------|------|\n| **LCP**\uff08\u6700\u5927\u5185\u5bb9\u7ed8\u5236\uff09 | \u52a0\u8f7d\u901f\u5ea6 | < 2.5s |\n| **FID**\uff08\u9996\u6b21\u8f93\u5165\u5ef6\u8fdf\uff09 | \u4ea4\u4e92\u6027 | < 100ms |\n| **CLS**\uff08\u7d2f\u8ba1\u5e03\u5c40\u504f\u79fb\uff09 | \u89c6\u89c9\u7a33\u5b9a\u6027 | < 0.1 |\n\n---\n\n## \u4e8c\u3001\u52a0\u8f7d\u4f18\u5316\n\n### 1. \u8d44\u6e90\u538b\u7f29\n\n```yaml\n\u4ee3\u7801\u538b\u7f29: webpack/vite \u7684 terser/esbuild\n\u56fe\u7247\u538b\u7f29: webp/avif \u683c\u5f0f\u66ff\u6362 png/jpg\nGzip/Brotli: \u670d\u52a1\u7aef\u5f00\u542f\u538b\u7f29\nTree Shaking: \u6d88\u9664\u672a\u4f7f\u7528\u7684\u4ee3\u7801\uff08ESM \u5929\u7136\u652f\u6301\uff09\n```\n\n### 2. \u61d2\u52a0\u8f7d\n\n```html\n\n\n\n\nconst Home = () => import('@/views/Home.vue')\n\n\n import('./Heavy.vue')\" />\n```\n\n### 3. \u9884\u52a0\u8f7d\n\n```html\n\n\n\n\n\n\n\n\n\n\n\n```\n\n---\n\n## \u4e09\u3001\u6e32\u67d3\u4f18\u5316\n\n### 1. \u51cf\u5c11\u56de\u6d41/\u91cd\u7ed8\uff08\u89c1 07-\u6d4f\u89c8\u5668\u6e32\u67d3.md\uff09\n\n### 2. \u865a\u62df\u5217\u8868\n\n```html\n\n\n```\n\n### 3. CSS \u4f18\u5316\n\n```css\n/* \u274c \u540e\u4ee3\u9009\u62e9\u5668\u6027\u80fd\u5dee */\n.container ul li a { }\n\n/* \u2705 \u76f4\u63a5\u7c7b\u9009\u62e9\u5668 */\n.nav-link { }\n\n/* \u2705 transform \u505a\u52a8\u753b\uff08\u4e0d\u8d70 layout/paint\uff09 */\n.animate { transition: transform 0.3s }\n```\n\n### 4. DOM \u64cd\u4f5c\u4f18\u5316\n\n```js\n// \u274c \u5faa\u73af\u4e2d\u9010\u4e2a\u63d2\u5165\nlist.forEach(item => parent.appendChild(createEl(item)))\n\n// \u2705 \u4f7f\u7528 DocumentFragment\nconst frag = document.createDocumentFragment()\nlist.forEach(item => frag.appendChild(createEl(item)))\nparent.appendChild(frag)\n```\n\n---\n\n## \u56db\u3001\u7f51\u7edc\u4f18\u5316\n\n### 1. CDN \u52a0\u901f\n### 2. HTTP/2 \u591a\u8def\u590d\u7528\n### 3. \u63a5\u53e3\u5408\u5e76 / BFF\n\n```js\n// \u274c \u591a\u4e2a\u4e32\u884c\u8bf7\u6c42\nconst a = await fetch('/a')\nconst b = await fetch('/b')\n\n// \u2705 \u5e76\u884c + \u5408\u5e76\nconst [a, b] = await Promise.all([fetch('/a'), fetch('/b')])\n```\n\n### 4. \u7f13\u5b58\u7b56\u7565\n\n```yaml\n\u8bb0\u5fc6\u5316/\u8ba1\u7b97\u7ed3\u679c\u7f13\u5b58: Vue computed, React useMemo\n\u8bf7\u6c42\u7f13\u5b58: SWR/tanstack-query \u7684 stale-while-revalidate\nService Worker: \u79bb\u7ebf\u7f13\u5b58\uff08PWA\uff09\n```\n\n---\n\n## \u4e94\u3001\u6253\u5305\u4f18\u5316\n\n| \u624b\u6bb5 | \u76ee\u7684 |\n|------|------|\n| \u4ee3\u7801\u5206\u5272\uff08Code Splitting\uff09 | \u6309\u8def\u7531\u62c6\u5206\uff0c\u51cf\u5c0f\u9996\u5c4f JS |\n| \u7b2c\u4e09\u65b9\u5e93\u6309\u9700\u5f15\u5165 | `import { pick } from 'lodash-es'` \u800c\u975e `import _ from 'lodash'` |\n| externals / CDN | React/Vue \u7b49\u7528 CDN \u5916\u94fe\uff0c\u4e0d\u8fdb bundle |\n| \u5206\u6790\u5de5\u5177 | webpack-bundle-analyzer / rollup-plugin-visualizer |\n\n---\n\n## \u516d\u3001Vue \u6027\u80fd\u4f18\u5316\n\n```vue\n\n
  • {{ item.name }}
  • \n\n\n\n\n\n \n \n```\n\n---\n\n## \u4e03\u3001\u9ad8\u9891\u9762\u8bd5\u9898\n\n### Q1\uff1a\u5982\u4f55\u4f18\u5316\u9996\u5c4f\u52a0\u8f7d\uff1f\n> \u8def\u7531\u61d2\u52a0\u8f7d + \u56fe\u7247\u61d2\u52a0\u8f7d + \u975e\u5173\u952e CSS/JS \u5ef6\u8fdf + \u9996\u5c4f\u5185\u5bb9 SSR/\u9884\u6e32\u67d3 + CDN + Gzip\u3002\n\n### Q2\uff1a\u5982\u4f55\u5904\u7406\u5927\u5217\u8868\u6e32\u67d3\uff1f\n> \u865a\u62df\u5217\u8868\uff08\u53ea\u6e32\u67d3\u53ef\u89c6\u533a\u57df\uff09\u3001\u5206\u9875\u52a0\u8f7d\u3001`requestAnimationFrame` \u5206\u7247\u6e32\u67d3\u3002\n\n### Q3\uff1awebpack \u4ee3\u7801\u5206\u5272\u600e\u4e48\u505a\uff1f\n> `import()` \u52a8\u6001\u5bfc\u5165\uff0c\u914d\u5408 `splitChunks` \u63d0\u53d6\u516c\u5171\u6a21\u5757\uff0c`entry` \u62c6\u5206\u591a\u5165\u53e3\u3002\n\n### Q4\uff1aTree Shaking \u7684\u524d\u63d0\u662f\u4ec0\u4e48\uff1f\n> \u5fc5\u987b\u662f **ESM**\uff08ES Module\uff09\u8bed\u6cd5\u3002CommonJS\uff08require\uff09\u65e0\u6cd5\u88ab Tree Shaking\uff08\u9759\u6001\u5206\u6790\u4e0d\u4e86\uff09\u3002\n\n---\n\n*\u6574\u7406\u65f6\u95f4\uff1a2026-06-03*\n", "12-\u5b89\u5168\u4e0e\u8de8\u57df": "# Web \u5b89\u5168 & \u8de8\u57df\u9762\u8bd5\u7b14\u8bb0\n\n---\n\n## \u4e00\u3001XSS\uff08\u8de8\u7ad9\u811a\u672c\u653b\u51fb\uff09\n\n### \u539f\u7406\n\n\u653b\u51fb\u8005\u5f80\u9875\u9762**\u6ce8\u5165\u6076\u610f\u811a\u672c**\uff0c\u7528\u6237\u6d4f\u89c8\u65f6\u811a\u672c\u6267\u884c\uff0c\u7a83\u53d6\u6570\u636e/\u7be1\u6539\u9875\u9762\u3002\n\n### \u4e09\u79cd\u7c7b\u578b\n\n| \u7c7b\u578b | \u653b\u51fb\u65b9\u5f0f | \u5e38\u89c1\u6765\u6e90 |\n|------|----------|----------|\n| \u5b58\u50a8\u578b | \u6076\u610f\u4ee3\u7801\u5b58\u5230\u6570\u636e\u5e93\uff0c\u6240\u6709\u8bbf\u95ee\u8005\u4e2d\u62db | \u8bc4\u8bba\u3001\u7559\u8a00 |\n| \u53cd\u5c04\u578b | \u6076\u610f\u4ee3\u7801\u901a\u8fc7 URL \u53c2\u6570\u53cd\u5c04\u56de\u9875\u9762 | \u641c\u7d22\u3001\u8df3\u8f6c |\n| DOM \u578b | \u524d\u7aef JS \u76f4\u63a5\u64cd\u4f5c DOM \u4ea7\u751f | innerHTML \u6ce8\u5165 |\n\n### \u9632\u5fa1\n\n```js\n// 1. \u8f93\u51fa\u7f16\u7801 / \u8f6c\u4e49\nfunction escapeHtml(str) {\n return str\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n}\n\n// 2. \u907f\u514d innerHTML / document.write\nel.textContent = userInput // \u2705 \u5b89\u5168\nel.innerHTML = userInput // \u274c \u5371\u9669\n\n// 3. CSP\uff08\u5185\u5bb9\u5b89\u5168\u7b56\u7565\uff09\n// Content-Security-Policy: default-src 'self'; script-src 'self'\n\n// 4. HttpOnly Cookie\uff08JS \u8bfb\u4e0d\u5230\uff09\n// Set-Cookie: token=xxx; HttpOnly; Secure; SameSite=Lax\n\n// 5. \u8f93\u5165\u6821\u9a8c & \u767d\u540d\u5355\n```\n\n---\n\n## \u4e8c\u3001CSRF\uff08\u8de8\u7ad9\u8bf7\u6c42\u4f2a\u9020\uff09\n\n### \u539f\u7406\n\n\u7528\u6237\u767b\u5f55 A \u7f51\u7ad9\u540e\uff0c\u88ab\u8bf1\u9a97\u8bbf\u95ee\u6076\u610f\u7f51\u7ad9 B\u3002B \u5229\u7528\u7528\u6237\u5728 A \u7684\u5df2\u767b\u5f55 Cookie\uff0c\u4f2a\u9020\u8bf7\u6c42\u5230 A\u3002\n\n```\n\u7528\u6237\u5df2\u767b\u5f55 bank.com\n \u2193\n\u8bbf\u95ee attacker.com\uff08\u6216\u70b9\u51fb\u9493\u9c7c\u94fe\u63a5\uff09\n \u2193\nattacker.com \u5077\u5077\u63d0\u4ea4
    \u5230 bank.com/transfer\n \u2193\n\u6d4f\u89c8\u5668\u81ea\u52a8\u9644\u5e26 bank.com \u7684 Cookie \u2192 \u8f6c\u8d26\u6210\u529f \ud83d\udc80\n```\n\n### \u9632\u5fa1\n\n```yaml\nToken \u6821\u9a8c: \u6bcf\u6b21\u8bf7\u6c42\u643a\u5e26\u670d\u52a1\u7aef\u751f\u6210\u7684 CSRF Token\nSameSite Cookie: Set-Cookie: SameSite=Strict\uff08\u8de8\u7ad9\u4e0d\u53d1\u9001 Cookie\uff09\nReferer/Origin \u6821\u9a8c: \u68c0\u67e5\u8bf7\u6c42\u6765\u6e90\n\u53cc\u91cd Cookie: \u8bf7\u6c42\u5934\u81ea\u5b9a\u4e49\u5b57\u6bb5\uff08\u975e\u7b80\u5355\u8bf7\u6c42\u89e6\u53d1 CORS\uff09\n\u9a8c\u8bc1\u7801/\u4e8c\u6b21\u786e\u8ba4: \u654f\u611f\u64cd\u4f5c\u589e\u52a0\u9a8c\u8bc1\n```\n\n---\n\n## \u4e09\u3001\u8de8\u57df & CORS\n\n### \u4ec0\u4e48\u662f\u8de8\u57df\uff1f\n\n**\u534f\u8bae\u3001\u57df\u540d\u3001\u7aef\u53e3**\u4efb\u4e00\u4e0d\u540c \u2192 \u8de8\u57df\uff08\u6d4f\u89c8\u5668\u540c\u6e90\u7b56\u7565\u9650\u5236\uff09\u3002\n\n### CORS\uff08\u8de8\u57df\u8d44\u6e90\u5171\u4eab\uff09\n\n```yaml\n# \u670d\u52a1\u7aef\u54cd\u5e94\u5934\nAccess-Control-Allow-Origin: https://example.com # \u5141\u8bb8\u7684\u6e90\nAccess-Control-Allow-Methods: GET, POST, PUT # \u5141\u8bb8\u7684\u65b9\u6cd5\nAccess-Control-Allow-Headers: Content-Type # \u5141\u8bb8\u7684\u81ea\u5b9a\u4e49\u5934\nAccess-Control-Allow-Credentials: true # \u5141\u8bb8\u643a\u5e26 Cookie\n```\n\n### \u7b80\u5355\u8bf7\u6c42 vs \u9884\u68c0\u8bf7\u6c42\n\n| \u7c7b\u578b | \u6761\u4ef6 | \u8fc7\u7a0b |\n|------|------|------|\n| \u7b80\u5355\u8bf7\u6c42 | GET/POST/HEAD + \u6807\u51c6\u5934 | \u76f4\u63a5\u53d1 \u2192 \u68c0\u67e5\u54cd\u5e94\u5934 |\n| \u9884\u68c0\u8bf7\u6c42 | \u5176\u4ed6\u65b9\u6cd5/\u81ea\u5b9a\u4e49\u5934/Content-Type \u975e\u6807\u51c6 | \u5148 OPTIONS \u2192 200 \u2192 \u518d\u53d1\u6b63\u5f0f\u8bf7\u6c42 |\n\n### \u5176\u4ed6\u8de8\u57df\u65b9\u6848\n\n```yaml\nJSONP: \n```\n\n---\n\n## \u56db\u3001\u8ba1\u7b97\u5c5e\u6027\n\n```jsx\n// React\uff1auseMemo\nconst double = useMemo(() => count * 2, [count])\n\n// Vue\uff1acomputed\uff08\u81ea\u52a8\u4f9d\u8d56\u8ffd\u8e2a\uff0c\u66f4\u7b80\u6d01\uff09\nconst double = computed(() => count.value * 2)\n```\n\n---\n\n## \u4e94\u3001\u6761\u4ef6/\u5217\u8868\u6e32\u67d3\n\n```jsx\n// React\uff1aJS \u8868\u8fbe\u5f0f\n{show &&
    shown
    }\n{items.map(item => )}\n\n// Vue\uff1a\u6307\u4ee4\n
    shown
    \n
    {{ item.name }}
    \n```\n\n---\n\n## \u516d\u3001\u7ec4\u4ef6\u901a\u4fe1\n\n| \u573a\u666f | React | Vue |\n|------|-------|-----|\n| \u7236\u2192\u5b50 | props | props / `defineProps` |\n| \u5b50\u2192\u7236 | \u56de\u8c03\u51fd\u6570 | `$emit` / `defineEmits` |\n| \u8de8\u5c42\u7ea7 | Context | provide / inject |\n| \u5168\u5c40 | Redux/Zustand | Pinia / Vuex |\n| \u53cc\u5411\u7ed1\u5b9a | \u624b\u52a8\uff1avalue + onChange | `v-model` |\n| \u63d2\u69fd | children / render props | `` / \u5177\u540d\u63d2\u69fd |\n\n---\n\n## \u4e03\u3001\u72b6\u6001\u7ba1\u7406\n\n| \u5bf9\u6bd4 | React \u751f\u6001 | Vue \u751f\u6001 |\n|------|-----------|----------|\n| \u4e3b\u6d41 | Redux / Zustand / Jotai | Pinia\uff08\u5b98\u65b9\uff09 |\n| \u7279\u70b9 | \u9700\u4e2d\u95f4\u4ef6\u5904\u7406\u5f02\u6b65 | \u539f\u751f\u652f\u6301 action \u5f02\u6b65 |\n| \u4ee3\u7801\u91cf | \u8f83\u591a\uff08\u6837\u677f\u4ee3\u7801\uff09 | \u8f83\u5c11 |\n| DevTools | Redux DevTools | Vue DevTools |\n\n---\n\n## \u516b\u3001\u751f\u547d\u5468\u671f\u5bf9\u6bd4\n\n| React\uff08class\uff09 | React\uff08hooks\uff09 | Vue\uff08Options\uff09 |\n|---------------|----------------|----------------|\n| componentDidMount | useEffect(fn, []) | mounted |\n| componentDidUpdate | useEffect(fn, [deps]) | updated |\n| componentWillUnmount | useEffect(() => fn, []) | beforeUnmount |\n| shouldComponentUpdate | React.memo | computed\uff08\u81ea\u52a8\uff09 |\n\n---\n\n## \u4e5d\u3001\u6027\u80fd\u5bf9\u6bd4\n\n| \u7ef4\u5ea6 | React | Vue |\n|------|-------|-----|\n| \u66f4\u65b0\u7c92\u5ea6 | \u7ec4\u4ef6\u7ea7 | \u7ec4\u4ef6\u7ea7 + \u7cbe\u786e\u5230\u53d8\u91cf |\n| \u4f18\u5316\u624b\u6bb5 | useMemo/useCallback/memo | \u81ea\u52a8\u8ffd\u8e2a\uff0c\u8f83\u5c11\u624b\u5199\u4f18\u5316 |\n| \u865a\u62df DOM | \u5168\u91cf diff | \u7f16\u8bd1\u65f6\u4f18\u5316\uff08\u9759\u6001\u63d0\u5347\u3001\u8865\u4e01\u6807\u8bb0\uff09 |\n| \u9996\u5c4f\u4f53\u79ef | ~40KB\uff08react + react-dom\uff09 | ~16KB\uff08runtime-only\uff09 |\n\n---\n\n## \u5341\u3001\u751f\u6001\n\n| \u9886\u57df | React | Vue |\n|------|-------|-----|\n| SSR/SSG | Next.js | Nuxt |\n| \u79fb\u52a8\u7aef | React Native | uni-app / taro |\n| \u684c\u9762\u7aef | Electron + React | Electron + Vue |\n| \u7ec4\u4ef6\u5e93 | Ant Design\u3001MUI | Element Plus\u3001Ant Design Vue |\n| \u7c7b\u578b\u652f\u6301 | TS \u539f\u751f\u53cb\u597d | Vue 3 + TS \u8f83\u597d |\n\n---\n\n## \u5341\u4e00\u3001\u9ad8\u9891\u9762\u8bd5\u9898\n\n### Q1\uff1aReact \u548c Vue \u6700\u5927\u7684\u533a\u522b\uff1f\n> React \u662f**\u4e0d\u53ef\u53d8\u6570\u636e + \u624b\u52a8 setState**\uff0cVue \u662f**\u54cd\u5e94\u5f0f\u6570\u636e + \u81ea\u52a8\u8ffd\u8e2a**\u3002React \u504f\u51fd\u6570\u5f0f\u3001\u9700\u663e\u5f0f\u4f18\u5316\uff1bVue \u504f\u58f0\u660e\u5f0f\u3001\u7f16\u8bd1\u5668\u5e2e\u505a\u4f18\u5316\u3002\n\n### Q2\uff1aVue \u7684 v-model \u5728 React \u4e2d\u600e\u4e48\u5b9e\u73b0\uff1f\n> React \u6ca1\u6709\u8bed\u6cd5\u7cd6\uff0c\u9700\u624b\u52a8\u4f20 `value` + `onChange`\uff0c\u6216\u7528\u7b2c\u4e09\u65b9\u5e93\u3002\u672c\u8d28\u662f\u53d7\u63a7\u7ec4\u4ef6\u7684\u6a21\u5f0f\u3002\n\n### Q3\uff1aReact \u7684 useEffect \u548c Vue \u7684 watch \u6709\u4ec0\u4e48\u533a\u522b\uff1f\n> useEffect \u662f**\u526f\u4f5c\u7528\u94a9\u5b50**\uff08\u6e32\u67d3\u540e\u6267\u884c\uff09\uff0c\u9700\u624b\u52a8\u58f0\u660e\u4f9d\u8d56\uff1bVue \u7684 watch \u662f**\u58f0\u660e\u5f0f\u76d1\u542c**\uff0c\u4f9d\u8d56\u81ea\u52a8\u8ffd\u8e2a\u3002computed \u66f4\u63a5\u8fd1 useMemo\u3002\n\n### Q4\uff1a\u4e3a\u4ec0\u4e48\u5927\u578b\u9879\u76ee\u9009 React \u66f4\u591a\uff1f\n> \u751f\u6001\u66f4\u4e30\u5bcc\uff08Next.js + TypeScript \u6df1\u5ea6\u6574\u5408\uff09\u3001\u51fd\u6570\u5f0f\u7f16\u7a0b\u5229\u4e8e\u62bd\u8c61\u4e0e\u6d4b\u8bd5\u3001React Native \u8de8\u7aef\u80fd\u529b\u3002\u4f46\u5b9e\u9645\u4e0a\u4e24\u8005\u90fd\u53ef\u4ee5\u652f\u6491\u5927\u578b\u9879\u76ee\u3002\n\n### Q5\uff1aVue 3 \u76f8\u6bd4 Vue 2 \u6700\u5927\u7684\u6539\u8fdb\uff1f\n> Composition API\uff08\u903b\u8f91\u590d\u7528\uff09\u3001Proxy \u54cd\u5e94\u5f0f\u3001\u66f4\u597d\u7684 TS \u652f\u6301\u3001Tree-shaking \u51cf\u80a5\u3002\n\n---\n\n*\u6574\u7406\u65f6\u95f4\uff1a2026-06-03*\n", "17-Vue\u8fdb\u9636": "# Vue \u8fdb\u9636\u9762\u8bd5\u7b14\u8bb0\n> \u57fa\u7840\u54cd\u5e94\u5f0f\u539f\u7406\u89c1 `05-Vue\u54cd\u5e94\u5f0f.md`\n\n---\n\n## \u4e00\u3001Composition API \u6df1\u5165\n\n### setup vs Options API\n\n```vue\n\n\n\n\n\n```\n\n### \u4e3a\u4ec0\u4e48\u5f15\u5165 Composition API\uff1f\n\n| \u95ee\u9898 | Options API | Composition API |\n|------|-------------|-----------------|\n| \u903b\u8f91\u590d\u7528 | Mixin\uff08\u547d\u540d\u51b2\u7a81/\u6765\u6e90\u4e0d\u6e05\uff09 | \u81ea\u5b9a\u4e49 Hook\uff08composables\uff09 |\n| \u4ee3\u7801\u7ec4\u7ec7 | \u6309\u9009\u9879\u62c6\u5206\uff08data/methods/watch \u5272\u88c2\uff09 | \u6309\u529f\u80fd\u805a\u5408\uff08\u76f8\u5173\u4ee3\u7801\u5728\u4e00\u8d77\uff09 |\n| TS \u652f\u6301 | \u5dee | \u597d |\n\n### composables\uff08\u81ea\u5b9a\u4e49 Hook\uff09\n\n```js\n// useMouse.js\nexport function useMouse() {\n const x = ref(0)\n const y = ref(0)\n function update(e) { x.value = e.pageX; y.value = e.pageY }\n onMounted(() => window.addEventListener('mousemove', update))\n onUnmounted(() => window.removeEventListener('mousemove', update))\n return { x, y }\n}\n\n// \u4f7f\u7528\nconst { x, y } = useMouse()\n```\n\n---\n\n## \u4e8c\u3001\u7ec4\u4ef6\u901a\u4fe1\u5927\u5168\n\n### 1. props / emits\n\n```vue\n\n\n\n\n\n```\n\n### 2. provide / inject\n\n```js\n// \u7956\u5148\nprovide('theme', 'dark')\n\n// \u540e\u4ee3\nconst theme = inject('theme')\n```\n\n### 3. v-model \u53cc\u5411\u7ed1\u5b9a\n\n```vue\n\n\n\n\n\n\n\n\n```\n\n### 4. ref + defineExpose\n\n```vue\n\n\n\n\n\n// childRef.value.reset()\n```\n\n### 5. \u4e8b\u4ef6\u603b\u7ebf\uff08\u4e0d\u63a8\u8350\uff09\n\n\u5c0f\u578b\u9879\u76ee\u53ef\u7528 `mitt` \u5e93\u505a\u7b80\u5355\u7684\u4e8b\u4ef6\u603b\u7ebf\u3002\n\n---\n\n## \u4e09\u3001\u63d2\u69fd\uff08Slot\uff09\n\n```vue\n\n Hello \n\n\n\n\n \n \n \n\n\n\n\n {{ item }} \n\n```\n\n---\n\n## \u56db\u3001Vue Router\n\n```js\nconst routes = [\n {\n path: '/user/:id',\n component: () => import('@/views/User.vue'), // \u61d2\u52a0\u8f7d\n meta: { requiresAuth: true },\n children: [/* \u5d4c\u5957\u8def\u7531 */]\n }\n]\n\n// \u5bfc\u822a\u5b88\u536b\nrouter.beforeEach((to, from) => {\n if (to.meta.requiresAuth && !token) return '/login'\n})\n```\n\n| \u5bf9\u6bd4 | hash | history |\n|------|------|---------|\n| URL | `/#/page` | `/page` |\n| \u539f\u7406 | hashchange \u4e8b\u4ef6 | History API (pushState) |\n| \u90e8\u7f72 | \u65e0\u9700\u670d\u52a1\u7aef\u914d\u7f6e | \u9700\u670d\u52a1\u7aef\u652f\u6301\uff08404 \u56de\u9000\u5230 index.html\uff09 |\n\n---\n\n## \u4e94\u3001Pinia\n\n```js\n// \u5b9a\u4e49 Store\nexport const useUserStore = defineStore('user', () => {\n const user = ref(null)\n const isLogin = computed(() => !!user.value)\n\n async function login(credentials) {\n user.value = await api.login(credentials)\n }\n\n return { user, isLogin, login }\n})\n\n// \u4f7f\u7528\nconst store = useUserStore()\nstore.login({ name, password })\n```\n\n### Pinia vs Vuex\n\n| \u5bf9\u6bd4 | Pinia | Vuex |\n|------|-------|------|\n| \u4f53\u79ef | ~1KB | \u8f83\u5927 |\n| TS \u652f\u6301 | \u539f\u751f | \u9700\u989d\u5916\u7c7b\u578b\u58f0\u660e |\n| Mutation | \u65e0\uff08\u76f4\u63a5\u6539 state\uff09 | \u6709 |\n| \u6a21\u5757 | \u591a\u4e2a\u72ec\u7acb store | modules \u5d4c\u5957 |\n| \u5b98\u65b9 | Vue 3 \u5b98\u65b9\u63a8\u8350 | Vue 2 \u5b98\u65b9 |\n\n---\n\n## \u516d\u3001KeepAlive\n\n```vue\n\n\n \n\n\n\n\n```\n\n---\n\n## \u4e03\u3001nextTick\n\n```js\n// \u6570\u636e\u53d8\u66f4\u540e\uff0cDOM \u8fd8\u6ca1\u66f4\u65b0\ncount.value++\nconsole.log(document.getElementById('num').textContent) // \u65e7\u503c\n\nawait nextTick()\n// DOM \u5df2\u66f4\u65b0\nconsole.log(document.getElementById('num').textContent) // \u65b0\u503c\n```\n\n> Vue \u7684 DOM \u66f4\u65b0\u662f**\u5f02\u6b65\u6279\u5904\u7406**\uff0c\u540c\u4e00\u4e8b\u4ef6\u5faa\u73af\u5185\u7684\u591a\u6b21\u6570\u636e\u53d8\u66f4\u53ea\u89e6\u53d1\u4e00\u6b21\u66f4\u65b0\u3002\n\n---\n\n## \u516b\u3001\u9ad8\u9891\u9762\u8bd5\u9898\n\n### Q1\uff1aMixin \u6709\u4ec0\u4e48\u95ee\u9898\uff1fComposition API \u5982\u4f55\u89e3\u51b3\uff1f\n> Mixin\uff1a\u547d\u540d\u51b2\u7a81\u3001\u6765\u6e90\u4e0d\u660e\u3001\u9690\u5f0f\u4f9d\u8d56\u3002Composition API\uff1a\u663e\u5f0f\u5f15\u5165\u3001\u51fd\u6570\u4f5c\u7528\u57df\u9694\u79bb\u3001TS \u53cb\u597d\u3002\n\n### Q2\uff1av-if \u548c v-show \u7684\u533a\u522b\uff1f\n> `v-if` \u63a7\u5236 DOM \u7684**\u5b58\u5728**\uff08\u771f\u6b63\u7684\u6761\u4ef6\u6e32\u67d3\uff09\uff0c\u5207\u6362\u5f00\u9500\u5927\uff1b`v-show` \u63a7\u5236 `display`\uff08\u59cb\u7ec8\u6302\u8f7d\uff09\uff0c\u521d\u59cb\u6e32\u67d3\u5f00\u9500\u5927\u3002\u9891\u7e41\u5207\u6362\u7528 v-show\u3002\n\n### Q3\uff1av-for \u4e3a\u4ec0\u4e48\u5fc5\u987b\u52a0 key\uff1f\n> key \u662f\u865a\u62df DOM \u7684\u552f\u4e00\u6807\u8bc6\uff0c\u5e2e\u52a9 diff \u7b97\u6cd5\u9ad8\u6548\u590d\u7528/\u79fb\u52a8 DOM \u8282\u70b9\u3002\u4e0d\u7528 index \u4f5c key \u56e0\u4e3a\u5217\u8868\u987a\u5e8f\u53d8\u5316\u65f6 index \u4e0d\u7a33\u5b9a\u3002\n\n### Q4\uff1aVue \u7ec4\u4ef6 data \u4e3a\u4ec0\u4e48\u5fc5\u987b\u662f\u51fd\u6570\uff1f\n> \u8fd4\u56de\u5bf9\u8c61\u7684\u5f15\u7528\u4f1a\u88ab\u6240\u6709\u5b9e\u4f8b\u5171\u4eab\u3002\u51fd\u6570\u8fd4\u56de\u65b0\u5bf9\u8c61\uff0c\u6bcf\u4e2a\u5b9e\u4f8b\u72ec\u7acb\u4e00\u4efd\u6570\u636e\u3002\n\n### Q5\uff1awatch \u548c computed \u7684\u9009\u62e9\uff1f\n> computed\uff1a\u4f9d\u8d56\u53d8\u5316\u65f6**\u8fd4\u56de**\u65b0\u503c\uff0c\u6709\u7f13\u5b58\uff0c\u4e0d\u6267\u884c\u526f\u4f5c\u7528\u3002watch\uff1a\u6570\u636e\u53d8\u5316\u65f6**\u6267\u884c**\u64cd\u4f5c\uff08API \u8bf7\u6c42\u3001DOM \u64cd\u4f5c\u7b49\u526f\u4f5c\u7528\uff09\u3002\n\n---\n\n*\u6574\u7406\u65f6\u95f4\uff1a2026-06-03*\n"}; let currentId = null; let sidebarOpen = false; // 渲染侧边栏 function renderList(filter = '') { const list = document.getElementById('note-list'); const filtered = NOTES.filter(n => n.title.toLowerCase().includes(filter.toLowerCase()) || n.id.includes(filter) ); list.innerHTML = filtered.map(n => ` ${n.title} `).join(''); } // 加载笔记内容(内嵌版,无需 fetch) function loadNote(id) { currentId = id; const note = NOTES.find(n => n.id === id); if (!note || !NOTES_CONTENT[id]) return; document.getElementById('topbar-title').textContent = note.title; const html = renderMarkdown(NOTES_CONTENT[id]); document.getElementById('content').innerHTML = `

    ${note.title}

    整理时间:${note.date}
    ${html}
    `; document.querySelectorAll('.sidebar-item').forEach(el => { el.classList.toggle('active', el.getAttribute('href') === '#' + id); }); if (window.innerWidth <= 768) toggleSidebar(false); window.scrollTo(0, 0); } // 简单 Markdown 渲染器 function renderMarkdown(md) { let html = md; // 代码块 html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => '
    ' + escapeHtml(code.trim()) + '
    ' ); // 行内代码 html = html.replace(/`([^`]+)`/g, '$1'); // 标题 html = html.replace(/^#### (.+)$/gm, '

    $1

    '); html = html.replace(/^### (.+)$/gm, '

    $1

    '); html = html.replace(/^## (.+)$/gm, '

    $1

    '); html = html.replace(/^# (.+)$/gm, '

    $1

    '); // 粗体 html = html.replace(/\*\*(.+?)\*\*/g, '$1'); // 表格 html = html.replace(/\|(.+)\|\n\|[-| :]+\|\n([\s\S]*?)(?=\n\n|\n###|\n##|\n#|$)/g, (match) => { const lines = match.trim().split('\n'); if (lines.length < 2) return match; const headers = lines[0].split('|').filter(c => c.trim()).map(c => `${c.trim()}`).join(''); const rows = lines.slice(2).map(line => { const cells = line.split('|').filter(c => c.trim()).map(c => `${c.trim()}`).join(''); return `${cells}`; }).join(''); return `${headers}${rows}
    `; }); // 列表 html = html.replace(/^[\s]*[-*] (.+)$/gm, '
  • $1
  • '); html = html.replace(/(
  • .*<\/li>)/s, '
      $1
    '); // 引用 html = html.replace(/^> (.+)$/gm, '
    $1
    '); // 分隔线 html = html.replace(/^---$/gm, '
    '); // 段落:将未包裹的文本行转为

    html = html.replace(/^(?!<[a-z])(.+)$/gm, '

    $1

    '); return html; } function escapeHtml(str) { return str.replace(/&/g,'&').replace(//g,'>'); } function filterNotes(val) { renderList(val); } function toggleSidebar(force) { sidebarOpen = force !== undefined ? force : !sidebarOpen; document.getElementById('sidebar').classList.toggle('open', sidebarOpen); document.getElementById('overlay').classList.toggle('show', sidebarOpen); } // 路由处理 function init() { renderList(); const hash = location.hash.slice(1); if (hash) { loadNote(hash); } } window.addEventListener('hashchange', () => { const hash = location.hash.slice(1); if (hash) loadNote(hash); }); init();